/*
============================================================================
 Name        : RssReader.cpp
 Author      : 
 Version     :
 Copyright   : Your copyright notice
 Description : CRssReader implementation
============================================================================
*/

#include "RssReader.h"

#include "XmlEngine.h"
#include "RssFeed.h"
#include "PluginUtility.h"

#include <f32file.h>

#define TIMEOUT 90000000
#define TIMER_TICK 100000

CRssReader::CRssReader(MReaderObserver& aObserver)
: CActive(EPriorityStandard), iObserver(aObserver), iState(ENotReady), iLastActionTime(0), iRssFeed(NULL), 
iUri(NULL)
{
}

CRssReader::~CRssReader()
{
	Cancel();
	iTimer.Close();
}

CRssReader* CRssReader::NewLC(const TDesC& aUri, MReaderObserver& aObserver)
{
	CRssReader* self = new (ELeave)CRssReader(aObserver);
	CleanupStack::PushL(self);
	self->ConstructL(aUri);
	return self;
}

CRssReader* CRssReader::NewL(const TDesC& aUri, MReaderObserver& aObserver)
{
	CRssReader* self=CRssReader::NewLC(aUri, aObserver);
	CleanupStack::Pop(); // self;
	return self;
}

void CRssReader::ConstructL(const TDesC& aUri)
{
	//Set url
	iUri = aUri.AllocL();

	//Set connection reference
	iConnection = CConnection::Static();

	//Initialize timer
	User::LeaveIfError(iTimer.CreateLocal());	
	CActiveScheduler::Add(this);
}

void CRssReader::CloseAndClean()
{
	//Clean	
	DELETE(iUri);
	DELETE(iRssFeed);

	iState = ENotReady;
}

void CRssReader::StartL()
{
	//Create rss feed
	iRssFeed = new TRssFeed();

	//Ready to get request
	iState = EReadyToGetRequest;

	//Activate
	iTimer.After(iStatus, TTimeIntervalMicroSeconds32(0));
	SetActive();
}

void CRssReader::GetRequestL()
{
	//Start retrieving http request
	iConnection->SetObserver(this);
	iConnection->RequestL(iUri->Des());

	//Reset timeout
	iLastActionTime = 0;

	iState = EReceivingData;
}

void CRssReader::RunL()
{
	TInt err = KErrNone;
	switch (iState)
	{
	case EReadyToGetRequest:
		TRAP(err, GetRequestL());
		if (err != KErrNone) iError.Copy(_L("Error getting http request."));
		break;

	case EReceivingData:
		//Do nothing
		if (iLastActionTime > TIMEOUT)
		{
			//Timeout
			err = -1;
			iError.Copy(_L("Timeout expired."));
		}
		else
		{
			iLastActionTime += TIMER_TICK;
		}
		break;

	case EReadOk:
		//Notify to observer
		iObserver.DocumentEnd();
		return;
	}

	//Check if there is any error
	if (err != KErrNone)
	{
		//Notify error to observer
		NotifyError(err);
		return;
	}

	iTimer.After(iStatus, TIMER_TICK);	// Set for 0.1 sec later
	SetActive();					// Tell scheduler a request is active
}

void CRssReader::DoCancel()
{
	iTimer.Cancel();
	iConnection->Cancel();
	CloseAndClean();

	iState = EReadFailed;
}

void CRssReader::ResponseRequestedL(TDesC8& aResponse)
{
	//Let's parse xml
	TRAPD(err, ParseXmlL(aResponse));
	if (err != KErrNone) 
	{
		iError.Copy(_L("Error parsing xml."));
		iState = EReadFailed;
	}
}

void CRssReader::ResponseErrorL(TInt aError)
{
	//Notify error to observer
	iError.Copy(_L("Http transaction failed."));
	NotifyError(aError);
}

void CRssReader::ParseXmlL(TDesC8& aResponse)
{
	//Parse xml response
	CXmlEngine* iXmlEngine = CXmlEngine::NewL(*this);
	TPtrC8 ptrC8((TUint8*)aResponse.Ptr(), aResponse.Size());
	iXmlEngine->ParseL(ptrC8, ETrue);
	DELETE(iXmlEngine);

	iState = EReadOk;
}

void CRssReader::StartElementRead(const TDesC& aNodeName, const RArray<TXmlAttribute> aAttributes)
{
	if (aNodeName.Compare(_L("rss/channel/item")) == 0)
	{
		iCurrentItem = new TRssFeedItem();
	}
	else if (aNodeName.Compare(_L("rss/channel/item/enclosure")) == 0)
	{
		TItemEnclosure* itemEnclosure = new TItemEnclosure();
		for (TInt i = 0; i < aAttributes.Count(); ++i)
		{
			if (aAttributes[i].iName.Compare(_L("url"))==0)
			{
				itemEnclosure->SetUrl(aAttributes[i].iValue);
			}
			else if (aAttributes[i].iName.Compare(_L("length"))==0)
			{
				TInt value;
				TLex lex(aAttributes[i].iValue);

				if (lex.Val(value) == KErrNone) value = 0;
				itemEnclosure->SetLength(value);
			}
			else if (aAttributes[i].iName.Compare(_L("type"))==0)
			{
				itemEnclosure->SetType(aAttributes[i].iValue);
			}
		}
		iCurrentItem->SetEnclosure(itemEnclosure);
	}
}

void CRssReader::EndElementRead(const TDesC& aNodeName, const TDesC& aData)
{
	if (aNodeName.Compare(_L("rss/channel/title")) == 0)
	{
		//Set title
		iRssFeed->SetTitle(aData);
	}
	else if (aNodeName.Compare(_L("rss/channel/description")) == 0)
	{
		//Set description
		iRssFeed->SetDescription(aData);
	}
	else if (aNodeName.Compare(_L("rss/channel/image/url")) == 0)
	{
		//Set image url
		iRssFeed->SetImageUrl(aData);
	}
	else if (aNodeName.Compare(_L("rss/channel/item/title")) == 0)
	{
		//Add title to item
		iCurrentItem->SetTitle(aData);
	}
	else if (aNodeName.Compare(_L("rss/channel/item/description")) == 0)
	{
		//Add description to item
		iCurrentItem->SetDescription(aData);
	}
	else if (aNodeName.Compare(_L("rss/channel/item")) == 0)
	{
		//Add item to list
		iRssFeed->AppendItemL(iCurrentItem);
	}
}

TRssFeed* CRssReader::GetRssFeed() 
{
	return iRssFeed;
}

void CRssReader::NotifyError(TInt aError)
{
	Cancel();
	iObserver.RunError(iError, aError);
}